/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#ifndef CORE_ROAACSREQUEST_H_
#define CORE_ROAACSREQUEST_H_

#include <string>

#include "AcsRequest.h"
#include "auth/RoaSignatureComposer.h"

namespace aliyun {

template<class T>
class RoaAcsRequest : public AcsRequest<T> {
 public:
  typedef T ResponseType;
  typedef AcsRequest<T> supper;  // parent class

  typedef std::string string;
  typedef http::FormatType FormatType;
  typedef http::HttpRequest HttpRequest;
  typedef auth::RoaSignatureComposer RoaSignatureComposer;

  RoaAcsRequest(string product)
      : supper(product) {
    initialize();
  }

  RoaAcsRequest(string product, string version)
      : supper(product, version) {
    this->setVersion(version);
    initialize();
  }

  RoaAcsRequest(string product, string version, string action)
      : supper(product) {
    this->setVersion(version);
    this->setActionName(action);
    initialize();
  }

  const string& getUriPattern() const {
    return uriPattern_;
  }

  void setUriPattern(string uriPattern) {
    uriPattern_ = uriPattern;
  }

  void setVersion(string version) {
    supper::setVersion(version);
    this->putHeaderParameter("x-acs-verion", version);
  }

  void setSecurityToken(string securityToken) {
    supper::setSecurityToken(securityToken);
    this->putHeaderParameter("x-acs-security-token", securityToken);
  }

  const std::map<string, string>& getPathParameters() {
    return pathParameters_;
  }

  // implement
  http::HttpRequest& signRequest(auth::ISigner* signer,
                                 const auth::Credential& credential,
                                 const http::FormatType format,
                                 const regions::ProductDomain& domain) {
    std::map<string, string> headers = this->getHeaders();
    if (NULL == signer) {
      string key = credential.getAccessKeyId();
      string secret = credential.getAccessSecret();
      headers = this->composer_->refreshSignParameters(this->getHeaders(),
                                                       signer, key, format);
      string strToSign = this->composer_->composeStringToSign(
          this->getMethod(), this->getUriPattern(), signer,
          this->getQueryParameters());
      string signature = signer->signString(strToSign, secret);
      headers["Authorization"] = "acs " + key + ":" + signature;
    }
    this->setUrl(
        this->compsoeUrl(domain.getDomainName(), this->getQueryParameters()));
    this->headers_ = headers;
    return this;
  }

  // implement
  string compsoeUrl(string endpoint, const std::map<string, string>& queries) {
    std::map<string, string> mapQueries = (
        queries.size() != 0 ? queries : this->getQueryParameters());

    string url = this->getProtocol().toString() + "://" + endpoint;
    if (uriPattern_.length() != 0) {
      url += RoaSignatureComposer::replaceOccupiedParameters(
          uriPattern_, this->getPathParameters());
    }

    if (url.find("?") == string::npos) {
      url.append("?");
    } else if (url.length() >= 1 && url[url.length() - 1] != '?') {
      url.append("&");
    }
    url += supper::concatQueryString(mapQueries);
    if (url[url.length() - 1] == '?' || url[url.length() - 1] == '&') {
      url.resize(url.length() - 1);
    }
    return url;
  }

 protected:
  void putPathParameter(string name, string value) {
    pathParameters_[name] = value;
  }

 private:

  void initialize() {
    this->setAcceptFormat(FormatType::RAW);
    this->composer_ = RoaSignatureComposer::getComposer();
  }

 protected:
  string uriPattern_;

 private:
  std::map<string, string> pathParameters_;
};

}

#endif  // CORE_ROAACSREQUEST_H_
